package mark.tom.index.compiler.utils;

import javax.lang.model.element.Element;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

import mark.tom.index.facade.enums.TypeKind;

import static mark.tom.index.compiler.utils.Consts.ARRAY_LIST;
import static mark.tom.index.compiler.utils.Consts.ARRAY_LIST_CHARSEQUENCE;
import static mark.tom.index.compiler.utils.Consts.ARRAY_LIST_INTEGER;
import static mark.tom.index.compiler.utils.Consts.ARRAY_LIST_STRING;
import static mark.tom.index.compiler.utils.Consts.BOOLEAN;
import static mark.tom.index.compiler.utils.Consts.BOOLEAN_ARRAY;
import static mark.tom.index.compiler.utils.Consts.BOOLEAN_PRIMITIVE;
import static mark.tom.index.compiler.utils.Consts.BYTE;
import static mark.tom.index.compiler.utils.Consts.BYTE_ARRAY;
import static mark.tom.index.compiler.utils.Consts.BYTE_PRIMITIVE;
import static mark.tom.index.compiler.utils.Consts.CHARSEQUENCE;
import static mark.tom.index.compiler.utils.Consts.CHARSEQUENCE_ARRAY;
import static mark.tom.index.compiler.utils.Consts.CHAR_ARRAY;
import static mark.tom.index.compiler.utils.Consts.CHAR_PRIMITIVE;
import static mark.tom.index.compiler.utils.Consts.DOUBLE;
import static mark.tom.index.compiler.utils.Consts.DOUBLE_ARRAY;
import static mark.tom.index.compiler.utils.Consts.DOUBLE_PRIMITIVE;
import static mark.tom.index.compiler.utils.Consts.FLOAT;
import static mark.tom.index.compiler.utils.Consts.FLOAT_ARRAY;
import static mark.tom.index.compiler.utils.Consts.FLOAT_PRIMITIVE;
import static mark.tom.index.compiler.utils.Consts.INTEGER;
import static mark.tom.index.compiler.utils.Consts.INT_ARRAY;
import static mark.tom.index.compiler.utils.Consts.INT_PRIMITIVE;
import static mark.tom.index.compiler.utils.Consts.LONG;
import static mark.tom.index.compiler.utils.Consts.LONG_ARRAY;
import static mark.tom.index.compiler.utils.Consts.LONG_PRIMITIVE;
import static mark.tom.index.compiler.utils.Consts.PARCELABLE;
import static mark.tom.index.compiler.utils.Consts.SERIALIZABLE;
import static mark.tom.index.compiler.utils.Consts.SHORT;
import static mark.tom.index.compiler.utils.Consts.SHORT_ARRAY;
import static mark.tom.index.compiler.utils.Consts.SHORT_PRIMITIVE;
import static mark.tom.index.compiler.utils.Consts.STRING;
import static mark.tom.index.compiler.utils.Consts.STRING_ARRAY;

public class TypeUtils {

    private Types types;
    private Elements elements;
    private TypeMirror parcelableType, serializableType;

    public TypeUtils(Types types, Elements elements) {
        this.types = types;
        this.elements = elements;

        parcelableType = this.elements.getTypeElement(PARCELABLE).asType();
        serializableType = this.elements.getTypeElement(SERIALIZABLE).asType();
    }

    public int typeExchange(Element element) {
        TypeMirror typeMirror = element.asType();
        String typeMirrorStr = typeMirror.toString();

        int result = TypeKind.OBJECT.ordinal();

        if (BOOLEAN_ARRAY.equals(typeMirrorStr)) {
            // boolean[]
            result = TypeKind.BOOLEAN_ARRAY.ordinal();
        } else if (BOOLEAN_PRIMITIVE.equals(typeMirrorStr) || BOOLEAN.equals(typeMirrorStr)) {
            // boolean || java.lang.Boolean
            result = TypeKind.BOOLEAN.ordinal();
        } else if (BYTE_ARRAY.equals(typeMirrorStr)) {
            // byte[]
            result = TypeKind.BYTE_ARRAY.ordinal();
        } else if (BYTE_PRIMITIVE.equals(typeMirrorStr) || BYTE.equals(typeMirrorStr)) {
            // byte || java.lang.Byte
            result = TypeKind.BYTE.ordinal();
        } else if (CHAR_ARRAY.equals(typeMirrorStr)) {
            // char[]
            result = TypeKind.CHAR_ARRAY.ordinal();
        } else if (CHAR_PRIMITIVE.equals(typeMirrorStr)) {
            // char
            result = TypeKind.CHAR.ordinal();
        } else if (CHARSEQUENCE_ARRAY.equals(typeMirrorStr)) {
            // CharSequence[]
            result = TypeKind.CHARSEQUENCE_ARRAY.ordinal();
        } else if (ARRAY_LIST_CHARSEQUENCE.equals(typeMirrorStr)) {
            // ArrayList<CharSequence>
            result = TypeKind.ARRAY_LIST_CHARSEQUENCE.ordinal();
        } else if (CHARSEQUENCE.equals(typeMirrorStr)) {
            // CharSequence
            result = TypeKind.CHARSEQUENCE.ordinal();
        } else if (DOUBLE_ARRAY.equals(typeMirrorStr)) {
            // double[]
            result = TypeKind.DOUBLE_ARRAY.ordinal();
        } else if (DOUBLE_PRIMITIVE.equals(typeMirrorStr) || DOUBLE.equals(typeMirrorStr)) {
            // double || java.lang.Double
            result = TypeKind.DOUBLE.ordinal();
        } else if (FLOAT_ARRAY.equals(typeMirrorStr)) {
            // float[]
            result = TypeKind.FLOAT_ARRAY.ordinal();
        } else if (FLOAT_PRIMITIVE.equals(typeMirrorStr) || FLOAT.equals(typeMirrorStr)) {
            // float
            result = TypeKind.FLOAT.ordinal();
        } else if (INT_ARRAY.equals(typeMirrorStr)) {
            // int[]
            result = TypeKind.INT_ARRAY.ordinal();
        } else if (ARRAY_LIST_INTEGER.equals(typeMirrorStr)) {
            // ArrayList<Integer>
            result = TypeKind.ARRAY_LIST_INTEGER.ordinal();
        } else if (INT_PRIMITIVE.equals(typeMirrorStr) || INTEGER.equals(typeMirrorStr)) {
            // int || java.lang.Integer
            result = TypeKind.INT.ordinal();
        } else if (LONG_ARRAY.equals(typeMirrorStr)) {
            // long[]
            result = TypeKind.LONG_ARRAY.ordinal();
        } else if (LONG_PRIMITIVE.equals(typeMirrorStr) || LONG.equals(typeMirrorStr)) {
            // long || java.lang.Long
            result = TypeKind.LONG.ordinal();
        } else if (SHORT_ARRAY.equals(typeMirrorStr)) {
            // short[]
            result = TypeKind.SHORT_ARRAY.ordinal();
        } else if (SHORT_PRIMITIVE.equals(typeMirrorStr) || SHORT.equals(typeMirrorStr)) {
            // short || java.lang.Short
            result = TypeKind.SHORT.ordinal();
        } else if (STRING_ARRAY.equals(typeMirrorStr)) {
            // String[]
            result = TypeKind.STRING_ARRAY.ordinal();
        } else if (ARRAY_LIST_STRING.equals(typeMirrorStr)) {
            // ArrayList<String>
            result = TypeKind.ARRAY_LIST_STRING.ordinal();
        } else if (STRING.equals(typeMirrorStr)) {
            // String
            result = TypeKind.STRING.ordinal();
        } else if (types.isSubtype(typeMirror, parcelableType)) {
            // Parcelable
            result = TypeKind.PARCELABLE.ordinal();
        } else if (typeMirror.getKind().toString().equals("ARRAY")) {
            // Parcelable[]
            TypeMirror targetMirror = null;
            try {
                targetMirror = this.elements.getTypeElement(typeMirrorStr.substring(0, typeMirrorStr.length() - 2)).asType();
            } catch (Exception ex) {
                // Do Nothing
            }
            if (targetMirror != null && types.isSubtype(targetMirror, parcelableType)) {
                result = TypeKind.PARCELABLE_ARRAY.ordinal();
            }
        } else if (typeMirror.toString().contains(ARRAY_LIST)) {
            // ArrayList<Parcelable>
            TypeMirror targetMirror = null;
            try {
                targetMirror = this.elements.getTypeElement(typeMirrorStr.subSequence(typeMirrorStr.indexOf("<") + 1, typeMirrorStr.indexOf(">"))).asType();
            } catch (Exception ex) {
                // Do Nothing
            }
            if (targetMirror != null && types.isSubtype(targetMirror, parcelableType)) {
                result = TypeKind.ARRAY_LIST_PARCELABLE.ordinal();
            }
        } else if (types.isSubtype(typeMirror, serializableType)) {
            // Serializable
            result = TypeKind.SERIALIZABLE.ordinal();
        }

        return result;
    }

}
